<!DOCTYPE HTML>
<html lang="en">
<head>
  <title>three.js - hatching - postprocessing - webgl</title>
  <meta charset="utf-8">
  <style type="text/css">
    body {
      color: #fff;
      font-family: Monospace;
      font-size: 13px;
      text-align: center;
      font-weight: bold;

      background-color: #000;
      margin: 0px;
      overflow: hidden;
    }

    #info {
      color: #fff;
      position: absolute;
      top: 0px;
      width: 100%;
      padding: 5px;

    }

    #oldie {
      font-family: monospace;
      font-size: 13px;

      text-align: center;
      background: #eee;
      color: #000;
      padding: 1em;

      width: 475px;
      margin: 5em auto 0;

      display: none;
    }

    a {
      color: red;
    }

  </style>
</head>
<body>

<div id="container"></div>

<center>
  <div id="oldie">
    Sorry, your browser doesn't support <a href="hxxp://khronos.org/webgl/wiki/Getting_a_WebGL_Implementation">WebGL</a> and <a href="hxxp://www.whatwg.org/specs/web-workers/current-work/">Web Workers</a>.
    <br/><br/>
    Please try in <a href="hxxp://www.chromium.org/getting-involved/dev-channel">Chrome 9+</a>/<a href="hxxp://www.mozilla.com/en-US/firefox/all-beta.html">Firefox 4+</a>/<a href="hxxp://nightly.webkit.org/">Safari OSX 10.6+</a>
  </div>
</center>

<script type="text/javascript" src="../shared/ThreeExtras.js"></script>
<script type="text/javascript" src="../shared/Stats.js"></script>


<!-- -----------------------------------------------------------------
      Convolution shader
        - ported from o3d sample to WebGL / GLSL
          http://o3d.googlecode.com/svn/trunk/samples/convolution.html
    ------------------------------------------------------------------>

<div id="main-code">

    <!-- Convolution fragment shader -->
<script id="fs-convolution" type="x-shader/x-fragment">
  varying vec2 vUv;

  uniform sampler2D tDiffuse;
  uniform vec2 uImageIncrement;

  #define KERNEL_SIZE 25
  uniform float cKernel[KERNEL_SIZE];

  void main(void) {
    vec2 imageCoord = vUv;
    vec4 sum = vec4( 0.0, 0.0, 0.0, 0.0 );
    for( int i=0; i < KERNEL_SIZE; ++i ) {
      sum += texture2D( tDiffuse, imageCoord ) * cKernel[i];
      imageCoord += uImageIncrement;
    }
    gl_FragColor = sum;
  }
</script>

<!-- Convolution vertex shader -->
<script id="vs-convolution" type="x-shader/x-vertex">
  varying vec2 vUv;

  uniform vec2 uImageIncrement;
  #define KERNEL_SIZE 25.0

  void main(void) {
    //vUv = vec2( uv.x, 1.0 - uv.y ) - ((KERNEL_SIZE - 1.0) / 2.0) *
    uImageIncrement;
    vUv = uv - ((KERNEL_SIZE - 1.0) / 2.0) * uImageIncrement;
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }
</script>


<!-- Render parameter modulated texture fragment shader -->
<script id="fs-screen" type="x-shader/x-fragment">
  varying vec2 vUv;
  uniform sampler2D tDiffuse;
  uniform float opacity;

  void main(void) {
    vec4 texel = texture2D( tDiffuse, vUv );
    gl_FragColor = opacity * texel;
  }
</script>

<!-- Generic vertex shader -->
<script id="vs-generic" type="x-shader/x-vertex">
  varying vec2 vUv;

  void main() {
    vUv = vec2( uv.x, 1.0 - uv.y );
    gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
  }
</script>


<script type="text/javascript">

if (!is_browser_compatible()) {
  document.getElementById("oldie").style.display = "block";
}

var ShaderTest = {

'hatching' : {

  uniforms: {

    "uDirLightPos":	{ type: "v3", value: new THREE.Vector3() },
    "uDirLightColor": { type: "c", value: new THREE.Color( 0xeeeeee ) },

    "uAmbientLightColor": { type: "c", value: new THREE.Color( 0x050505 ) },

    "uBaseColor":  { type: "c", value: new THREE.Color( 0xffffff ) },
    "uLineColor1": { type: "c", value: new THREE.Color( 0x000000 ) },
    "uLineColor2": { type: "c", value: new THREE.Color( 0x000000 ) },
    "uLineColor3": { type: "c", value: new THREE.Color( 0x000000 ) },
    "uLineColor4": { type: "c", value: new THREE.Color( 0x000000 ) }

  },

  vertex_shader: [

    "varying vec3 vNormal;",

    "void main() {",

      "gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );",
      "vNormal = normalize( normalMatrix * normal );",

    "}"

  ].join("\n"),

  fragment_shader: [

    "uniform vec3 uBaseColor;",
    "uniform vec3 uLineColor1;",
    "uniform vec3 uLineColor2;",
    "uniform vec3 uLineColor3;",
    "uniform vec3 uLineColor4;",

    "uniform vec3 uDirLightPos;",
    "uniform vec3 uDirLightColor;",

    "uniform vec3 uAmbientLightColor;",

    "varying vec3 vNormal;",

    "void main() {",

      "float directionalLightWeighting = max( dot( normalize(vNormal), uDirLightPos ), 0.0);",
      "vec3 lightWeighting = uAmbientLightColor + uDirLightColor * directionalLightWeighting;",

      "gl_FragColor = vec4( uBaseColor, 1.0 );",

      "if ( length(lightWeighting) < 1.00 ) {",

        "if ( mod(gl_FragCoord.x + gl_FragCoord.y, 10.0) == 0.0) {",

          "gl_FragColor = vec4( uLineColor1, 1.0 );",

        "}",

      "}",

      "if ( length(lightWeighting) < 0.75 ) {",

        "if (mod(gl_FragCoord.x - gl_FragCoord.y, 10.0) == 0.0) {",

          "gl_FragColor = vec4( uLineColor2, 1.0 );",

        "}",
      "}",

      "if ( length(lightWeighting) < 0.50 ) {",

        "if (mod(gl_FragCoord.x + gl_FragCoord.y - 5.0, 10.0) == 0.0) {",

          "gl_FragColor = vec4( uLineColor3, 1.0 );",

        "}",
      "}",

      "if ( length(lightWeighting) < 0.3465 ) {",

        "if (mod(gl_FragCoord.x - gl_FragCoord.y - 5.0, 10.0) == 0.0) {",

          "gl_FragColor = vec4( uLineColor4, 1.0 );",

        "}",
      "}",

    "}"

  ].join("\n")

}

};

var container, stats;

var cameraOrtho, cameraPerspective, sceneRTT, sceneScreen, sceneBG, renderer, mesh, directionalLight;

var windowHalfX = window.innerWidth / 2;
var windowHalfY = window.innerHeight / 2;

var rtTexture, materialScreen, materialConvolution, blurx, blury, quadBG, quadScreen;

init();
setInterval(loop, 1000 / 60);

function init() {
  container = document.getElementById('container');

  cameraOrtho = new THREE.Camera();
  cameraOrtho.projectionMatrix = THREE.Matrix4.makeOrtho(window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, -10000, 10000);
  cameraOrtho.position.z = 100;

  cameraPerspective = new THREE.Camera(50, window.innerWidth / window.innerHeight, 1, 10000);
  cameraPerspective.position.z = 900;

  sceneRTT = new THREE.Scene();
  sceneScreen = new THREE.Scene();
  sceneBG = new THREE.Scene();

  directionalLight = new THREE.DirectionalLight(0xffffff);
  directionalLight.position.x = 0;
  directionalLight.position.y = 0;
  directionalLight.position.z = 1;
  directionalLight.position.normalize();
  sceneRTT.addLight(directionalLight);

  rtTexture1 = new THREE.RenderTarget(window.innerWidth, window.innerHeight, { min_filter: THREE.LinearFilter, mag_filter: THREE.LinearFilter });
  rtTexture2 = new THREE.RenderTarget(256, 512, { min_filter: THREE.LinearFilter, mag_filter: THREE.LinearFilter });
  rtTexture3 = new THREE.RenderTarget(512, 256, { min_filter: THREE.LinearFilter, mag_filter: THREE.LinearFilter });

  materialScreen = new THREE.MeshShaderMaterial({

    uniforms: { tDiffuse: { type: "t", value: 0, texture: rtTexture1 },
      opacity: { type: "f", value: 0.4 }
    },
    vertex_shader: document.getElementById('vs-generic').textContent,
    fragment_shader: document.getElementById('fs-screen').textContent,
    blending: THREE.AdditiveBlending

  });

  var kernel = buildKernel(4.0);

  blurx = new THREE.Vector2(0.001953125, 0.0),
  blury = new THREE.Vector2(0.0, 0.001953125);

  materialConvolution = new THREE.MeshShaderMaterial({

    uniforms: { tDiffuse: { type: "t", value: 0, texture: rtTexture1 },
      uImageIncrement: { type: "v2", value: blury },
      cKernel: { type: "fv1", value: kernel }
    },
    vertex_shader: document.getElementById('vs-convolution').textContent,
    fragment_shader: document.getElementById('fs-convolution').textContent

  });

  var plane = new Plane(window.innerWidth, window.innerHeight);

  shader = ShaderTest["hatching"];

  material1 = new THREE.MeshShaderMaterial({

    uniforms: Uniforms.clone(shader.uniforms),
    vertex_shader: shader.vertex_shader,
    fragment_shader: shader.fragment_shader

  });

  material2 = new THREE.MeshShaderMaterial({

    uniforms: Uniforms.clone(shader.uniforms),
    vertex_shader: shader.vertex_shader,
    fragment_shader: shader.fragment_shader

  });

  material1.uniforms.uDirLightPos.value = material2.uniforms.uDirLightPos.value = directionalLight.position;
  material2.uniforms.uDirLightColor.value = material2.uniforms.uDirLightColor.value = directionalLight.color;

  var lineColor1 = 0xff0000, lineColor2 = 0x0000ff;
  material1.uniforms.uBaseColor.value.setHex(0x000000);
  material1.uniforms.uLineColor1.value.setHex(lineColor1);
  material1.uniforms.uLineColor2.value.setHex(lineColor1);
  material1.uniforms.uLineColor3.value.setHex(lineColor1);
  material1.uniforms.uLineColor4.value.setHex(0xffff00);

  material2.uniforms.uBaseColor.value.setHex(0x000000);
  material2.uniforms.uLineColor1.value.setHex(lineColor2);
  material2.uniforms.uLineColor2.value.setHex(lineColor2);
  material2.uniforms.uLineColor3.value.setHex(lineColor2);
  material2.uniforms.uLineColor4.value.setHex(0x00ffff);

  loader = new THREE.Loader(true);
  document.body.appendChild(loader.statusDomElement);
  loader.loadAscii({ model: "../shared/leeperrysmith/LeePerrySmith.js",
    callback: function(geometry) {
      createMesh(geometry, sceneRTT, 100)
    } });

  quadScreen = new THREE.Mesh(plane, materialConvolution);
  quadScreen.position.z = -100;
  sceneScreen.addObject(quadScreen);

  renderer = new THREE.WebGLRenderer();
  renderer.setSize(window.innerWidth, window.innerHeight);
  renderer.autoClear = false;
  container.appendChild(renderer.domElement);

  stats = new Stats();
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.top = '0px';
  //container.appendChild( stats.domElement );
}

function createMesh(geometry, scene, scale) {

  var mesh1 = new THREE.Mesh(geometry, material1);
  mesh1.scale.x = mesh1.scale.y = mesh1.scale.z = scale;
  mesh1.position.x = -300;
  mesh1.position.y = -50;
  scene.addObject(mesh1);

  mesh = new THREE.Mesh(geometry, material2);
  mesh.scale.x = mesh.scale.y = mesh.scale.z = scale;
  mesh.rotation = mesh1.rotation;
  mesh.position.x = 300;
  mesh.position.y = -50;
  scene.addObject(mesh);

  loader.statusDomElement.style.display = "none";

}

/*****************************************************************************************/
// Convolution
//  - ported from o3d convolution shader sample
//      http://o3d.googlecode.com/svn/trunk/samples/convolution.html
/*****************************************************************************************/

  // We lop off the sqrt(2 * pi) * sigma term, since we're going to normalize anyway.
function gauss(x, sigma) {
  return Math.exp(- (x * x) / (2.0 * sigma * sigma));
}

function buildKernel(sigma) {

  var kMaxKernelSize = 25;
  var kernelSize = 2 * Math.ceil(sigma * 3.0) + 1;
  if (kernelSize > kMaxKernelSize) kernelSize = kMaxKernelSize;
  var halfWidth = ( kernelSize - 1 ) * 0.5

  var values = new Array(kernelSize);
  var sum = 0.0;
  for (var i = 0; i < kernelSize; ++i) {

    values[ i ] = gauss(i - halfWidth, sigma);
    sum += values[ i ];

  }

  // normalize the kernel
  for (var i = 0; i < kernelSize; ++i) values[ i ] /= sum;

  return values;

}

var start = 0;

function loop() {

  if (! start) start = new Date().getTime();
  var time = ( new Date().getTime() - start ) * 0.0004;

  if (mesh) {

    mesh.rotation.y = -time;

  }

  renderer.clear();

  // Render scene into texture

  renderer.context.disable(renderer.context.DEPTH_TEST);
  renderer.render(sceneBG, cameraOrtho, rtTexture1);
  renderer.context.enable(renderer.context.DEPTH_TEST);

  renderer.render(sceneRTT, cameraPerspective, rtTexture1);

  // Render quad with blured scene into texture (convolution pass 1)

  quadScreen.materials = [ materialConvolution ];
  materialConvolution.uniforms.tDiffuse.texture = rtTexture1;
  materialConvolution.uniforms.uImageIncrement.value = blurx;
  renderer.render(sceneScreen, cameraOrtho, rtTexture2);

  // Render quad with blured scene into texture (convolution pass 2)

  materialConvolution.uniforms.tDiffuse.texture = rtTexture2;
  materialConvolution.uniforms.uImageIncrement.value = blury;
  renderer.render(sceneScreen, cameraOrtho, rtTexture3);

  // Render original scene with superimposed blur to texture

  quadScreen.materials = [ materialScreen ];

  materialScreen.uniforms.tDiffuse.texture = rtTexture3;
  materialScreen.uniforms.opacity.value = 1.5;
  renderer.render(sceneScreen, cameraOrtho, rtTexture1, false);

  // Render to screen

  materialScreen.uniforms.tDiffuse.texture = rtTexture1;
  renderer.render(sceneScreen, cameraOrtho);

  stats.update();

}

function is_browser_compatible() {

  // WebGL support

  try {
    var test = new Float32Array(1);
  } catch(e) {
    return false;
  }

  // Web workers

  return !!window.Worker;

}


</script>
</div>
</body>
</html>
